﻿/*压缩类型：完全*/
/********************************************************************
功能：流程图部分功能在kOA系统上的具体实现
使用：
	作为流程图的扩展部分，在父窗口中指定，并由flowchart_panel.js引入
实现：
	流程图预留接口的以下函数
		FlowChartObject.DrawFlow
		FlowChartObject.CheckFlow_Extend
		FlowChart_Node_ShowAttribute
		FlowChart_Line_ShowAttribute
作者：叶中奇
创建时间：2005-06-01
修改记录：
	2005-06-16：完成了kOA流程配置的扩充功能
********************************************************************/

//====================::::::::::常量/公用函数::::::::::====================

//sbbok//刷新节点node的显示样式
function FlowChart_KOA_RefreshNodeView(node)   
{
    var stepName = GetAttribute(node.Data.xml,"StepName").text;
    //var stepTypeName = GetAttribute(node.Data.xml,"StepTypeName").text;
    
	node.SetText( stepName );
	
	//var title = "：";
	//title += "<br>处理人：" ;
	//title += "<br>处理方式：";
	//node.SetTitle(title);
}


//====================::::::::::初始化调用函数::::::::::====================
//sbbok//根据文档信息展现流程
function FlowChartObject.DrawFlow()
{
    //debugger;

    //注意，此处刷新图形有所修改，刷新图形采用reload这个iframe方式刷新，要不会有问题的。
	FlowChartObject.Points.Hide();
	for(; FlowChartObject.Nodes.all.length>0; )
	{
		FlowChartObject.Nodes.all[0].Delete(true);
		
	}
    //sbb 在node.all.delete时不会删除flowchartobject中的startnode 和 endnode，需要手动清除。
    if (FlowChartObject.Nodes.Start) FlowChartObject.Nodes.Start= null;
    if (FlowChartObject.Nodes.End) FlowChartObject.Nodes.End = null;
	FlowChartObject.Current.SelectElement(null, true, true);
	FlowChartObject.Nodes.Index = 0;
	
	
	//若原表格设置的节点个数为0，不初始化数据
	var mynodes = parent.xmlDom.selectNodes("//Step");
	var nodesLen = mynodes.length;
	if(nodesLen==0)
		return;
	
	var i, j, index, node;
	var posHadSet = true;					//位置信息是否完整
	var jumpCon;							//跳转条件
	
		
	//读取其它节点的信息
	for(i=0; i<nodesLen; i++)
	{
	
	    //判断节点类型增加
	    switch( mynodes(i).selectSingleNode("Attributes/StepTypeName").text)
	    {
	        case "开始":
	            node = new FlowChart_Node("start");
	            break;
	        case "结束":
	            node = new FlowChart_Node("end");
	            break;
	        case "审批":
				node = new FlowChart_Node("sp");
				break;
			case "会签":
				node = new FlowChart_Node("hq");
				break;
	        //default:
	            //node = new FlowChart_Node();
	    }
        //给节点赋属性值
		node.Data.xml = mynodes(i).xml ;
	
			
		node.Data.rowIndex = 0;
		node.Data.colIndex = 0;
		node.Data.parentNode = null;
		
		node.Data.jumpInfo = new Array;
		
		var relations = mynodes(i).selectNodes("Relations//Relation");
		 
		for(j=0; j<relations.length; j++)
		{
		    var expression = relations(j).selectSingleNode("Attributes/Expression").text;
		    var nextstepid = parseInt(relations(j).selectSingleNode("Attributes/NextStepID").text) - 1;
		    var expressionstru = relations(j).selectSingleNode("Attributes/ExpressionStru").text;		    
		    var position = relations(j).selectSingleNode("Attributes/Position").text;
			var relationguid = relations(j).selectSingleNode("Attributes/RelationGUID").text;
			var remark = relations(j).selectSingleNode("Attributes/Remark").text;
		    
		    if (position=="") 
		    {   
		        posHadSet = false;
		        posHadSet = "NodeBottom,NodeTop";
		    }
		    
			node.Data.jumpInfo[j] = new Array( expression ,nextstepid ,expressionstru , j,relationguid, remark);
		}
		
		//已经走过的步骤实例不允许修改 added by xug 2008-05-21 
		var stepstatus = mynodes(i).selectSingleNode("Attributes/StepStatus").text;
		if(stepstatus!="" && stepstatus!="0")
		{
			node.CanDelete = false;
			node.CanChgAttribute = false;
		}
		//end
		
		//更新节点显示样式
		FlowChart_KOA_RefreshNodeView(node);
		//记录节点位置是否完整
		if(posHadSet &&  mynodes(i).selectSingleNode("Attributes/XYPos").text=="")
			posHadSet = false;
	}
	
	//==========调整节点位置并创建相应的连线==========
	if(posHadSet)	//原保存的节点位置信息是完整的，根据节点位置信息调整节点位置
		FlowChart_KOA_INI_DrawView();
	else			//原保存的节点位置信息没有或可能被修改过，重新生成节点位置信息
		FlowChart_KOA_INI_RebuildView();
}




//sbbok//根据原有的位置信息画流程图
function FlowChart_KOA_INI_DrawView()
{
	var i, j, node, position;
	for(i=0; i<FlowChartObject.Nodes.all.length ; i++)
	{
		node = FlowChartObject.Nodes.all[i];
		/**********************************************************************
		F_NodePosition的信息格式：
			x,y;连线1起始位置,连线1结束位置;连线2起始位置,连线2结束位置;……
			其中连线为该节点引出的连线
		**********************************************************************/
		var strpos = GetAttribute(node.Data.xml,"XYPos").text + GetNodePositionString(node.Data.xml);
		position = strpos.split(";");
		for(j=0; j<position.length; j++)
			position[j] = position[j].split(",");
		node.MoveTo(parseInt(position[0][0]), parseInt(position[0][1]));
		if(node.Type!="end")
			FlowChart_KOA_INI_AddLine(node, position);
			
	}
}


//sbbok
/**************************************************************************************************
功能：将表格信息翻译成流程图
实现思路：
	1、遍历所有节点，将下一个节点所处界面行位置（rowIndex）置为上一节点之后
		注意判断若有些节点可能会往前跳转，这种情况不能做上述处理
	2、节点所处界面的行号已经确定，创建节点间的连线
	3、根据行号，从上到下调整节点显示的位置。节点的x坐标根据上一节点确定先后，并作居中处理
**************************************************************************************************/
function FlowChart_KOA_INI_RebuildView()
{

	var i, j, index, node, nextNode;
	var maxrowindex=0;
	//====================获取每个节点的rowIndex====================
	for(i=0; i<FlowChartObject.Nodes.all.length; i++)
	{
		node = FlowChartObject.Nodes.all[i];
		for(j=0; j<node.Data.jumpInfo.length; j++)
		{
			//node2为当前节点将要跳转到的节点
			nextNode = FlowChartObject.Nodes.all[node.Data.jumpInfo[j][1]];
			
			if(nextNode.Data.rowIndex<=node.Data.rowIndex)
			{
				//node2位置在node前面，可能需要调整
				if(i>node.Data.jumpInfo[j][1])
				{
				/******************************************************************************
				node节点往前面的节点node2跳转，检查node2的跳转信息
				若node2的所有跳转都跳转到node后，说明node2应该放在node的后面
				但若调整了node2的位置，则node2后面节点的位置可能就需要重新调整，所以将i重置
				******************************************************************************/
					for(index = 0; index<nextNode.Data.jumpInfo.length; index++)
						if(i>=nextNode.Data.jumpInfo[index][1])
							break;
					if(index==nextNode.Data.jumpInfo.length)
					{
						//把node2放在node的上面，记录parentNode是为了同一行的节点排序，返回前面的节点运行
						nextNode.Data.parentNode = node;
						nextNode.Data.rowIndex = node.Data.rowIndex + 1;
						
						i = node.Data.jumpInfo[j][1]-1;
						break;
					}
				}else
				{
					//把node2放在node的上面，记录parentNode是为了同一行的节点排序
					nextNode.Data.parentNode = node;
					nextNode.Data.rowIndex = node.Data.rowIndex + 1;
				}
				
				if (maxrowindex < nextNode.Data.rowIndex) maxrowindex = nextNode.Data.rowIndex;
			}
		}
	}
	
	//定义开始和结束节点的位置
	FlowChartObject.Nodes.Start.Data.rowIndex = 0;
	//sbb这行代码似乎有点问题，如果倒数第二个节点不是排在最后的情况下图形会乱
	//FlowChartObject.Nodes.End.Data.rowIndex = FlowChartObject.Nodes.all[FlowChartObject.Nodes.all.length-1].Data.rowIndex+1;
	FlowChartObject.Nodes.End.Data.rowIndex = maxrowindex ;
	
	/******************************************************************************************
	为了方便计算，下面函数将节点的位置保存在一个二维数组rows中
	若rows[i][j]==node，说明node这个节点出现在流程图的第i+1行第j+1列
	rows[i][j]的位置将在后面的函数中根据上一个节点的位置再次排序
	在遍历的过程，将连线接上
	******************************************************************************************/
	var rows = new Array();
	for(i=0; i<FlowChartObject.Nodes.all.length; i++)
	{
		node = FlowChartObject.Nodes.all[i];
		//sbb11 如果节点又position信息则使用默认的position信息,暂时实现有困难
		FlowChart_KOA_INI_AddLine(node );
		j = node.Data.rowIndex;
		if(rows[j]==null)
			rows[j] = new Array;
		rows[j][rows[j].length] = node;
	}
	
	//====================移动节点位置====================
	var dx = 50;			//节点列间隔
	var dy = 50;			//节点行间隔
	//移动开始/结束节点位置
	var x = Math.round((document.body.clientWidth-node.GetWidth())/2);
	//FlowChartObject.Nodes.Start.MoveTo(x, 20, true);
	//FlowChartObject.Nodes.End.MoveTo(x, 120, true);
	var y = FlowChartObject.Nodes.Start.Top+FlowChartObject.Nodes.Start.GetHeight();
	//由于rowIndex==0的节点是起始节点，所以rows[0]==null，所以下面的循环从1开始
	for(i=0; i<rows.length; i++)
	{
		//起始坐标=(界面的宽度-所有节点的宽度-节点中间间隙的宽度)/2
		//sbb11 能出现rows数组中有空节点的情况
		if (rows[i]!=null)
		{
		    x = Math.round((document.body.clientWidth-rows[i].length*rows[i][0].GetWidth()-(rows[i].length-1)*dx)/2);
		    //根据上一节点的位置排序
		    rows[i].sort(FlowChart_KOA_INI_SortPosition);
		    for(j=0; j<rows[i].length; j++)
		    {
			    //设置当前节点的位置，为后面的排序使用
			    rows[i][j].Data.colIndex = j;
			    
			    //sbb 如果节点本身有xy位置，则按照本身的xy位置进行处理
			    var nodexy = GetAttribute(rows[i][j].Data.xml,"XYPos").text;
			    if (nodexy!="")
			    {
			        nodexy = nodexy.split(",")
			        rows[i][j].MoveTo(nodexy[0], nodexy[1], true);
			    }
			    else
			    {
			        rows[i][j].MoveTo(x, y, true);
			    }
			    x = rows[i][j].Left+rows[i][j].GetWidth()+dx;
		    }
		    y = rows[i][0].Top+rows[i][0].GetHeight()+dy;
		 }

	}
}



//sbbok
/**********************************************************
功能：排序函数
返回值：
	>0：node2排在node1后面
	=0：不改变顺序
	<0：node1排在node2后面
**********************************************************/
function FlowChart_KOA_INI_SortPosition(node1, node2)
{
	if(node1.Data.parentNode==null)
		return 1;
	if(node2.Data.parentNode==null)
		return -1;
	return node1.Data.parentNode.Data.colIndex - node2.Data.parentNode.Data.colIndex;
}




//sbbok
/**********************************************************
功能：创建node1的连线
参数：
	node1：引出连线的节点
	position：二维数组（n>0）
		position[0][0]：节点x坐标
		position[0][1]：节点y坐标
		position[n][0]：节点引出的第n条连线的起点位置
		position[n][1]：节点引出的第n条连线的终点位置
**********************************************************/
function FlowChart_KOA_INI_AddLine(node1, position)
{
    //debugger;
	var line, node2;
	for(var i=0; i<node1.Data.jumpInfo.length; i++)
	{
		node2 = FlowChartObject.Nodes.all[node1.Data.jumpInfo[i][1]];
		line = new FlowChart_Line;
		if(position==null || position[i+1]==null)
		{
			if( parseInt(GetAttribute(node2.Data.xml,"StepID").text) < parseInt( GetAttribute(node1.Data.xml,"StepID").text) )
			{
				//node1处于node2之下
				line.SetStartPoint("link", node1, "NodeTop");
				line.SetEndPoint("link", node2, "NodeBottom");
			}else
			{
				//node1处于node2之上
				line.SetStartPoint("link", node1, "NodeBottom");
				line.SetEndPoint("link", node2, "NodeTop");
			}
		}else
		{
			line.SetStartPoint("link", node1, position[i+1][0]);
			line.SetEndPoint("link", node2, position[i+1][1]);
		}
		line.SetText(node1.Data.jumpInfo[i][0]);
		//sbb11 给线赋属性
		line.Data.ExpressionStru = node1.Data.jumpInfo[i][2];
		line.Data.RelationId = node1.Data.jumpInfo[i][3];
		line.Data.RelationGUID = node1.Data.jumpInfo[i][4];
		//wf 给线赋属性
		line.Data.Remark = node1.Data.jumpInfo[i][5];
	}
}




//====================::::::::::过程调用函数::::::::::====================
//替换原有的设置节点属性的函数
function FlowChart_Node_ShowAttribute()
{
    if(this.CanChgAttribute)
	{
        FlowChartObject.SaveFlow();
        
        var stepid ;
	    for (var i=0;i< FlowChartObject.Nodes.all.length;i++)
	    {
	        if ( FlowChartObject.Nodes.all[i] == this )
	        {
	            stepid = i + 1;
	            break;
	        }
	    }
		
        parent.ChangeStepAttribute(stepid,"Graph");
	}
}

//替换原有的设置连线属性的函数
function FlowChart_Line_ShowAttribute()
{
	//debugger;
    if(this.CanChgAttribute)
	{
        FlowChartObject.SaveFlow();
        
        var stepid ,relationid
	    for (var i=0;i< FlowChartObject.Nodes.all.length;i++)
	    {
	        if ( FlowChartObject.Nodes.all[i] == this.StartPoint.Node )
	        {
	            stepid = i + 1;
	            break;
	        }
	    }
		
        relationid = this.Data.RelationId;
        parent.EditRelation_Graph(stepid,relationid);
	}
    
}


//流程检查的扩展函数
function FlowChartObject.CheckFlow_Extend()
{

}

/**********************************************************
功能：检查流程循环点
参数：
	node：检查起始点
	arr：当前检查的路线经过的节点列表
	allarr：曾经被检查过的节点
**********************************************************/
function FlowChart_KOA_CheckFlow_Cyc(node, arr, allarr)
{
	
}


//====================::::::::::提交调用函数::::::::::====================
var FlowChart_KOA_GridRow;			//全局变量，图形流程节点在表格显示的行号
/******************************************************************************
功能：检查流程配置是否合理，并将流程信息写入到表单的域中
返回：
	true：成功写入
	false：写入失败
******************************************************************************/
function FlowChartObject.SaveFlow()
{

    
	if(!FlowChartObject.HadInited)
		return true;
	var i, j, node;
	
	//检测流程配置是否合理
	//if(!FlowChartObject.CheckFlow(true))
		//return false;
	
    //0 重写XML,注意node.Data.xml不能为空
    
    //1 循环处理所有Node的XYPos到xml中
    //1.1删除掉XML文件中所有的Step重新生成
    var mynodes = parent.xmlDom.selectNodes("//Step");
    var xmlnodes = parent.xmlDom.selectSingleNode("//Steps");
	mynodes.removeAll();
	
	var objnodes = FlowChartObject.Nodes.all
    for (i=0;i<objnodes.length;i++)
    {
    
       if (objnodes[i].Data.xml ==null )
       {
          var xmlDomStep = parent.moduleDom.selectSingleNode("xml/Step").cloneNode(true); 
          
          objnodes[i].Data.xml = xmlDomStep.xml;
          //nodedom = SetAttribute(objnodes[i].Data.xml , "StepName" ,objnodes[i].Text );
       }
    
    
       var nodedom 
       nodedom = SetAttribute(objnodes[i].Data.xml , "StepName" ,objnodes[i].Text );

       var g = top.GetGUID();
       if (g.length==36&&GetAttribute(nodedom.xml,"StepGUID").text=="")   nodedom = SetAttribute(nodedom.xml , "StepGUID" ,g );
	   //if (g.length==36)   nodedom = SetAttribute(nodedom.xml , "StepGUID" ,g );
       
       switch(objnodes[i].Type)
		{
			case "start":
			nodedom = SetAttribute(nodedom.xml , "StepType" ,"0" );			
			nodedom = SetAttribute(nodedom.xml , "StepTypeName" ,"开始" );
			break;
			
			case "sp":
			nodedom = SetAttribute(nodedom.xml , "StepType" ,"1" );
			nodedom = SetAttribute(nodedom.xml , "StepTypeName" ,"审批" );
			break;
			
			case "hq":
			nodedom = SetAttribute(nodedom.xml , "StepType" ,"2" );
			nodedom = SetAttribute(nodedom.xml , "StepTypeName" ,"会签" );
			break;
			
			case "end":
			nodedom = SetAttribute(nodedom.xml , "StepType" ,"3" );
			nodedom = SetAttribute(nodedom.xml , "StepTypeName" ,"结束" );
			break;
			
		}

       
       nodedom = SetAttribute(nodedom.xml , "XYPos" ,objnodes[i].Left.toString() + "," + objnodes[i].Top.toString() );
       nodedom = SetAttribute(nodedom.xml , "StepID" ,i + 1 );
       
       
       
       
       //处理线条
       var xmlrelations = nodedom.selectSingleNode("Step/Relations");
       nodedom.selectNodes("//Relation").removeAll();

       var objrelations = objnodes[i].LineStart;
       
       //debugger;
       for (j=0;j<objrelations.length;j++)
       {
           var relationDom = parent.moduleDom.selectSingleNode("xml/Relation").cloneNode(true); 
           
           relationDom.selectSingleNode("Attributes/Expression").text = objrelations[j].Text;

           if (objrelations[j].Data.ExpressionStru)
               relationDom.selectSingleNode("Attributes/ExpressionStru").text = objrelations[j].Data.ExpressionStru;
           else
               relationDom.selectSingleNode("Attributes/ExpressionStru").text = "";

		   if (objrelations[j].Data.RelationGUID)
               relationDom.selectSingleNode("Attributes/RelationGUID").text = objrelations[j].Data.RelationGUID;
               
           if (objrelations[j].Data.Remark)
               relationDom.selectSingleNode("Attributes/Remark").text = objrelations[j].Data.Remark;
               
           relationDom.selectSingleNode("Attributes/IsActive").text = "1";
           relationDom.selectSingleNode("Attributes/Position").text =  objrelations[j].StartPoint.Position + "," + objrelations[j].EndPoint.Position;
           
           
           for (var m=0 ; m < objnodes.length ; m++)
           {
                if (objnodes[m] == objrelations[j].EndPoint.Node )
                {
                    relationDom.selectSingleNode("Attributes/NextStepID").text = m + 1;
                    break;
                }
           }
           
		   xmlrelations.appendChild(relationDom);
        }
              
       objnodes[i].Data.xml = nodedom.xml;
       xmlnodes.appendChild(nodedom.documentElement);
    }
    

	for (var k=0; k<FlowChartObject.Lines.all.length ; k++ )
	{
	   FlowChartObject.Lines.all[k].Data.RelationId = k ;
	}
	
	return true;
}



//==========================通用方法======================================
//sbbok//通用获取XMl中属性的方法
function GetAttribute( data , attrName )
{
    //debugger;
    var attrDom = new ActiveXObject("Microsoft.XMLDOM"); 
    attrDom.async=false; 
    attrDom.loadXML(data);
    
    if(attrDom.parseError.errorCode != 0) 
        return undefined;
    else
        return attrDom.selectSingleNode("//" + attrName);
}


//sbbok//通用获取XMl中属性的方法
function SetAttribute( data , attrName ,value )
{
    var attrDom = new ActiveXObject("Microsoft.XMLDOM"); 
    attrDom.async=false; 
    attrDom.loadXML(data);
    
    if(attrDom.parseError.errorCode == 0) 
        attrDom.selectSingleNode("//" + attrName).text = value;
    
    return attrDom;
}


//sbbok
function GetCollections( data , collsName )
{
    var collsDom = new ActiveXObject("Microsoft.XMLDOM"); 
    collsDom.async=false; 
    collsDom.loadXML(data);
    
    if(collsDom.parseError.errorCode != 0) 
        return undefined;
    else
        return collsDom.selectNodes("//" + collsName);

}

//sbbok
function GetNodePositionString( data )
{
    var strpos = "";
	var relations = GetCollections(data,"Relation");
	
	for(j=0;j <relations.length;j++)
	{
	    if (relations(j).selectSingleNode("Attributes/Position").text=="")
	        strpos = strpos + ";" + "NodeBottom,NodeTop";
	    else
    	    strpos = strpos + ";" + relations(j).selectSingleNode("Attributes/Position").text;
	}

    return strpos;
}


function setNodeText(stepGUID)
{
	var node = FlowChartObject.Current.all[0];
	var html = "";
	
	var bandsDom = new ActiveXObject("Microsoft.XMLDOM"); 
    bandsDom.async=false; 
    bandsDom.loadXML(node.Data.xml);

    var steptypename="";
    var line = "------------------------------------------------<br>";
    var stepstatus = bandsDom.selectSingleNode("//NodeStatus");

    if (stepstatus != null) 
        stepstatus =stepstatus.text; 
    else 
        stepstatus = "M";

    if ((stepstatus != "3" && stepstatus != "-3" && stepstatus != "-1") || parent.$id("txtHandleXml") == null)
	{
	    if (stepstatus != "M")
	    {
	        var bands = bandsDom.selectNodes("//Band");
	        for (var i=0;i<bands.length;i++)
		        html += "<img src='/images/workflow/member.gif'>" + bands(i).selectSingleNode("Attributes/BandName").text + "<br>";
	    }	    
	}
	else
	{
	    var dom = new ActiveXObject("Microsoft.XMLDOM"); 
        dom.async=false; 
        dom.loadXML(parent.$id("txtHandleXml").value);
        var hands = dom.selectNodes("//steppath[@stepguid='" + stepGUID + "' and @isHis='0' and @st!='0']/node");
	    if (hands.length > 0)
	        html = "<table style='z-index:6;background-color:#FFFFEE;width:270px;font-size:12px;' borderColor='#EFEFEB' cellSpacing='0' cellPadding='1' border='1'>";

	    for(var i=0;i<hands.length;i++)
	    {
	        hand = hands[i].selectNodes("hd");
	        var txt;
	        html += "<tr><td>"+hands(i).getAttribute("auditor") + "</td><td>";
	        for (var j=0;j<hand.length;j++)
	        {
		        html += hand(j).getAttribute("date") +" "+ hand(j).getAttribute("handler") + " "+hand(j).getAttribute("action")+"<br>";
	        }
	        html+="</td></tr>"
	        
	    }
	    if (hands.length > 0)
	        html+= "</table>"
	    title +=html;
	    line = "";
	}
	
	var title ="";
	if (node.Data.xml)
	{	
	    stepstatus = GetAttribute(node.Data.xml,"StepStatusName").text;
		steptypename = GetAttribute(node.Data.xml,"StepTypeName").text;
	}
	else
	    stepstatus = "";
	    
	title +=steptypename + "步骤&nbsp;&nbsp;" + stepstatus + "<br>";
	title +=line;
	title +=html ;
	
	node.SetTitle(title);
}